package com.example.my_helper;

import android.content.Context;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.Queue;

import android.os.CpuUsageInfo;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import cn.wch.ch34xuartdriver.CH34xUARTDriver;

public class MyTCPClient {

    final static int ON_CONNECT = 0;
    final static int ON_MESSAGE = 1;
    final static int ON_SEND = 2;
    final static int ON_CLOSE = 3;
    final static int ON_ERROR = 4;
    final static int NOT_FOUND = 5;
    final static int NO_PERMISSION = 6;
    final static int CONFIG_ERROR = 7;
    final static int UNSUPPORT = 8;

    public String host;
    public int port;
    public Socket socket;
    public InputStream inputStream;
    public OutputStream outputStream;

    public Context context;
    public Handler handler;

    public boolean isConnected = false;
    public int buf_len = 0;
    public int recv_len = 0;
    public byte[] buf = new byte[4096];
    private int recv_tick = 0;
    private boolean connect_lock = false;
    //Queue<Byte> recv_queue;
    NetworkDebuger networkDebuger;

    public MyTCPClient(Context context, Handler handler)
    {
        this.context = context;
        this.handler = handler;
        //this.recv_queue = new LinkedList<>();
    }

    public void Connect(String host, int port)
    {
        this.host = host;
        this.port = port;
        if(connect_lock)
            return;
        connect_lock = true;
        new Thread(new Runnable() {
            @Override
            public void run() {
                if(isConnected && socket != null) {
                    isConnected = false;
                    try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

                try {
                    socket = new Socket(host, port);
                    outputStream = socket.getOutputStream();
                    inputStream = socket.getInputStream();
                    isConnected = true;
                    handler.sendEmptyMessage(ON_CONNECT);
                    new recv_thread().start();
                    connect_lock = false;
                } catch (Exception e) {
                    e.printStackTrace();
                    handler.sendEmptyMessage(ON_ERROR);
                    connect_lock = false;
                }
            }
        }).start();
    }

    /*public int Read(byte[] buf, int len, int timeout)
    {
        return Read(buf, 0, len, timeout);
    }


    public int Read(byte[] buf, int offset, int len, int timeout)
    {
        int i = 0;
        int size;
        while(true)
        {
            size = recv_queue.size();
            for(int t = 0; t < size; t++)
            {
                buf[i++] = recv_queue.poll();
                if(i == len)
                    return len;
            }
            if(timeout == 0 || !isConnected)
            {
                return i;
            }
            else if(timeout > 0)
            {
                timeout--;
            }
            try{
                Thread.sleep(1);
            }catch (Exception e)
            {
                e.printStackTrace();
            }

        }
    }

    public String Read(int timeout)
    {
        byte[] buf = new byte[1024];
        int len = Read(buf, 0, buf.length, timeout);
        if(len == 0)
            return null;
        else
            return new String(buf, 0, len);
    }

    public byte ReadByte()
    {
        while(true)
        {
            if(!recv_queue.isEmpty())
                return recv_queue.poll();
            if(isConnected)
                return -1;
        }
    }

    public String ReadLine()
    {
        byte[] buf = new byte[1024];
        int i = 0;
        while(true)
        {
            buf[i++] = ReadByte();
            if(i > buf.length || buf[i] == -1)
                return null;
            if(i > 0 && buf[i] == '\n' && buf[i-1] == '\r')
                return new String(buf, 0, i);
        }
    }*/

    public int Write(byte[] bytes, int len)
    {
        return Write(bytes,0 , len);
    }

    public int Write(byte[] bytes, int offset, int len)
    {
        if (!isConnected || outputStream == null) {
            return -1;
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {   //发送
                    outputStream.write(bytes, offset, len);
                    outputStream.flush();
                    handler.sendEmptyMessage(ON_SEND);
                } catch (Exception e) {
                    e.printStackTrace();
                    handler.sendEmptyMessage(ON_ERROR);
                    Close();
                }
            }
        }).start();
        return 0;
    }

    public int Write(String string)
    {
        return Write(string.getBytes(), 0,  string.length());
    }

    public int WriteByte(byte b)
    {
        byte[] bytes = new byte[1];
        bytes[0] = b;
        return Write(bytes, 0,  1);
    }

    public int WriteLine(String string)
    {
        byte[] buf = (string+"\r\n").getBytes();
        return Write(buf, 0, buf.length);
    }

    /*public int Available(){
        return recv_queue.size();
    }*/

    public void Close()
    {
        if(isConnected)
        {
            try{
                if(socket != null){
                    socket.close();
                    socket = null;
                }
                if(outputStream != null) {
                    outputStream.close();
                    outputStream = null;
                }
                if(inputStream != null) {
                    inputStream.close();
                    inputStream = null;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            isConnected = false;
            handler.sendEmptyMessage(ON_CLOSE);
        }
    }

/*    private void put(byte b)
    {
        if (recv_queue.size() > buf_len) {
            recv_queue.poll();
        }
        if (!recv_queue.offer(b)) {
            recv_queue.poll();
            recv_queue.offer(b);
        }
    }*/

    class recv_thread extends Thread {
        public void run() {
            byte[] tmp = new byte[1024];
            int len;
            byte b;

            while (isConnected) {
                try {
                    b = (byte) inputStream.read();
                    if(b >= 0) {
                        tmp[buf_len++] = b;
                        while (true) {
                            len = inputStream.available();
                            if (len > 0) {
                                recv_tick = 0;
                                for (int i = 0; i < len; i++) {
                                    b = (byte) inputStream.read();
                                    if(b < 0)
                                        break;
                                    tmp[buf_len++] = b;
                                    if(buf_len >= 1024) {
                                        handler.removeMessages(ON_MESSAGE);
                                        networkDebuger.Log_recv(tmp, buf_len);
                                        buf_len = 0;
                                    }
                                }
                            } else {
                                recv_tick++;
                                if(recv_tick > 20){
                                    handler.removeMessages(ON_MESSAGE);
                                    networkDebuger.Log_recv(tmp, buf_len);
                                    buf_len = 0;
                                    break;
                                }
                                Thread.sleep(1);
                            }
                        }
                    } else {
                        Close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    Close();
                }
            }
        }
    }
}
